package com.wbk.lotterysystemuserservice.service.serviceImpl;

import cn.hutool.crypto.SecureUtil;
import com.wbk.lotterysystemuserservice.commons.bo.UserRegisterBO;
import com.wbk.lotterysystemuserservice.entity.User;
import com.wbk.lotterysystemuserservice.entity.UserExample;
import com.wbk.lotterysystemuserservice.mapper.UserMapper;
import com.wbk.lotterysystemuserservice.service.UserInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tools.NOUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    UserMapper userMapper;

    @Override
    public Map<String, Object> loginUserLock(String username) {
        Map<String, Object> map = new HashMap<>();
        String key = "loginLock:" + username;
        if (stringRedisTemplate.hasKey(key)) {
            Long expire = stringRedisTemplate.getExpire(key, TimeUnit.MINUTES);
            String reason = stringRedisTemplate.opsForValue().get(key);
            map.put("flag", true);
            map.put("expire", expire);
            map.put("reason", reason);
        } else {
            map.put("flag", false);
        }
        return map;
    }

    @Override
    public String loginError(String username) {
        int time = 5;//规定时间内
        int num = 5;//允许密码错误次数
        String key = "loginCount:" + username;
        if (!stringRedisTemplate.hasKey(key)) {//没有key，表示第一次错误
            stringRedisTemplate.opsForValue().set(key, "1", time, TimeUnit.MINUTES);
            return "在" + time + "分钟内还允许失败" + (num - 1) + "次";
        } else {//key存在，表示第二次以上错误
            long count = Long.parseLong(stringRedisTemplate.opsForValue().get(key));
            if (count < (num - 1)) {//还允许错误
                stringRedisTemplate.opsForValue().increment(key, 1);
                return "在" + time + "分钟内还允许失败" + (num - 1 - count) + "次";
            } else {//超过次数
                stringRedisTemplate.opsForValue().set("loginLock:" + username, "密码错误次数达到5次", 1, TimeUnit.HOURS);
                return "因失败次数达到" + num + "次，您已被封禁1个小时";
            }
        }
    }

    @Cacheable(cacheNames = "user::login", key = "#username+'-'+#password")
    @Override
    public User getUserByUsernameAndPassword(String username, String password) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andUsernameEqualTo(username);
        criteria.andPasswordEqualTo(SecureUtil.md5(password));
        List<User> users = userMapper.selectByExample(userExample);
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0);
    }

    @Cacheable(cacheNames = "user::byMobile", key = "#mobile")
    @Override
    public User getUserByMobile(String mobile) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andMobileEqualTo(mobile);
        List<User> users = userMapper.selectByExample(userExample);
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0);
    }

    @Cacheable(cacheNames = "user::byEmail", key = "#email")
    @Override
    public User getUserByEmail(String email) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andEmailEqualTo(email);
        List<User> users = userMapper.selectByExample(userExample);
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0);
    }

    @Cacheable(cacheNames = "user::byUsername", key = "#username")
    @Override
    public User getUserByUsername(String username) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andUsernameEqualTo(username);
        List<User> users = userMapper.selectByExample(userExample);
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0);
    }

    @Cacheable(cacheNames = "user::byUserNo", key = "#userNo")
    @Override
    public User getUserByUserNo(Long userNo) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andUserNoEqualTo(userNo);
        List<User> users = userMapper.selectByExample(userExample);
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0);
    }

    @Override
    public boolean codeExpire(String email, String code) {
        String value = stringRedisTemplate.opsForValue().get("code:" + email);
        return code.equals(value);
    }

    @Caching(evict = {
            @CacheEvict(cacheNames = "user::byUserNo", key = "#result.userNo"),
            @CacheEvict(cacheNames = "user::byEmail", key = "#result.email"),
            @CacheEvict(cacheNames = "user::byMobile", key = "#result.mobile"),
            @CacheEvict(cacheNames = "user::login", key = "#result.username+'-'+#record.password"),
            @CacheEvict(cacheNames = "user::byUsername", key = "#result.username")
    })
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public User save(UserRegisterBO record) {
        User user = new User();
        BeanUtils.copyProperties(record, user);
        user.setPassword(SecureUtil.md5(record.getPassword()));
        user.setUserNo(NOUtil.getUserNo());
        userMapper.insertSelective(user);
        return user;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int reset(User user) {
        return userMapper.updateByPrimaryKeySelective(user);
    }

}
